/* 
 * Author: anand
 * File: miniplayer.cpp
 *
 * This is just the beginning... the beginning of the end ;)
 */
#include <conio.h>
#include <process.h>
#include "playlist.h"
#include <string>
#include "application.h"
#include <dshow.h>

#define BUFSIZE 4096


bool _isPaused;
long _isMuted;
playlist * _list;
application * _application;
IGraphBuilder *pGraph = NULL;
IMediaControl *pControl = NULL;
IMediaEvent   *pEvent = NULL;
IBasicAudio   *pBasicAudio = NULL;
HANDLE _mediaEventsThread = NULL;

wchar_t* GetWChar(const char * achar){
	int len = strlen(achar)+1;
	wchar_t *res = new wchar_t[len];
	memset(res,0,len);
	MultiByteToWideChar(CP_ACP,NULL,achar,-1,res,len);
	return res;
}

void __cdecl GraphEventsThreadProc(void * pParam);


bool reinit_mediaplayer(const std::string toPlay, bool restartThread)
{

    if (!toPlay.empty())
    {
        long volume = 0;
        if(pBasicAudio) 
        {
            pBasicAudio->get_Volume(&volume);
        }
        if(pControl){
            pControl->Stop();
        }
        if(pControl)
        {
            pControl->Release();
            pEvent->Release();
            pBasicAudio->Release();
        }
           
        HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, 
                      IID_IGraphBuilder, (void **)&pGraph);
        if (FAILED(hr))
        {
                printf("ERROR - Could not create the Filter Graph Manager.");
                exit(EXIT_FAILURE);

        }
        pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
        pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
        pGraph->QueryInterface(IID_IBasicAudio,(void **)&pBasicAudio);
        if(_mediaEventsThread && restartThread)
        {
            TerminateThread(_mediaEventsThread,EXIT_SUCCESS);
            _mediaEventsThread = (HANDLE) _beginthread(GraphEventsThreadProc,0,NULL);

        }
        if (SUCCEEDED(pGraph->RenderFile(GetWChar(toPlay.c_str()), NULL)))
        {
            pBasicAudio->put_Volume(volume);
            pControl->Run();
            return true;
        }
        else
            return false;

    }
    else
    {
        return false;
    }
}

void ProcessInput(char ch){

    long volume = 0;
    std::string toPlay;
	switch(_application->get_key_val(ch)){
		case KeyMapValue::VOLUME_UP :
            
            pBasicAudio->get_Volume(&volume);
            if(volume < 0)
                pBasicAudio->put_Volume(volume + 250);

			break;
		case KeyMapValue::VOLUME_DOWN :
            pBasicAudio->get_Volume(&volume);
            if(volume > -10000)
                pBasicAudio->put_Volume(volume - 250);
			break;
		case KeyMapValue::QUIT :
			printf("You killed me....... :(\n");
			exit(EXIT_FAILURE);
			break;
		case KeyMapValue::PAUSE :
            if(_isPaused)
                pControl->Run();
            else
                pControl->Pause();
            _isPaused = !_isPaused;

			break;
		case KeyMapValue::MUTE :
            if(_isMuted != -10000){
                pBasicAudio->put_Volume(_isMuted);
                _isMuted = -10000;
            }
            else
            {
                pBasicAudio->get_Volume(&_isMuted);
                pBasicAudio->put_Volume(-10000);
            }
            
			break;
		case KeyMapValue::SHOW_PLAYLIST :
			_list->show_current_playlist();
			break;
		case KeyMapValue::SHOW_FUNCTIONS:
			_application->display_function_list();
			break;
		case KeyMapValue::PLIST_DELETE:
			printf("REMOVING FILE from playlist only,UNIMP\n");
			break;
		case KeyMapValue::FSYS_DELETE:
			printf("REMOVING FILE,UNIMP\n");
			break;

        case KeyMapValue::NEXT:
            toPlay = _list->next_item();
            if(reinit_mediaplayer(toPlay,true))
                printf("Playing: %s\n",toPlay.c_str());
            break;

        case KeyMapValue::PREV:
            toPlay = _list->prev_item();
            if(reinit_mediaplayer(toPlay,true))
                printf("Playing: %s\n",toPlay.c_str());
            break;

		default:
			printf("No Bind found for %c(%d). Press h for help\n",ch,ch);
	}
}


void __cdecl IdleThreadProc(void * pParam){

	HANDLE hPipe = CreateFile(L"\\\\.\\pipe\\mynamedpipe",
		GENERIC_WRITE,
		0,
		NULL,
		OPEN_EXISTING,
		0,
		NULL);
	DWORD cbWritten,cbBytesRead;
	BOOL fConnected = 0, fSuccess = 1; 
	TCHAR* pchRequest = new TCHAR [BUFSIZE];
	if(hPipe == INVALID_HANDLE_VALUE)
	{
		//We are here first
		//printf("Idle Thread Creating pipe\n");

		//http://msdn.microsoft.com/en-us/library/aa365588(VS.85).aspx
		hPipe = CreateNamedPipe(L"\\\\.\\pipe\\mynamedpipe",
			PIPE_ACCESS_INBOUND,       // read/write access 
			PIPE_TYPE_MESSAGE |       // message type pipe 
			PIPE_READMODE_MESSAGE |   // message-read mode 
			PIPE_WAIT,                // blocking mode 
			PIPE_UNLIMITED_INSTANCES, // max. instances  
			BUFSIZE,                  // output buffer size 
			BUFSIZE,                 // input buffer size 
			0,                        // client time-out 
			NULL);                    // default security attribute 
		if (hPipe == INVALID_HANDLE_VALUE) 
		{
			printf("CreateNamedPipe failed, GLE=%d.\n", GetLastError()); 
			return;
		}
		while(fSuccess){
			fConnected = ConnectNamedPipe(hPipe, NULL);
			fSuccess = ReadFile( 
				hPipe,        // handle to pipe 
				pchRequest,    // buffer to receive data 
				BUFSIZE*sizeof(TCHAR), // size of buffer 
				&cbBytesRead, // number of bytes read 
				NULL);        // not overlapped I/O 
			DisconnectNamedPipe(hPipe); //<=this is important for reading in a loop
			char *dataRead = new char[cbBytesRead+1];
			memcpy(dataRead,pchRequest,cbBytesRead);
			dataRead[cbBytesRead]='\0';
			printf("Data read :%s \n",dataRead);

		}
	}else
	{
		//printf("Idle Thread Writing to pipe\n");

		//We will surely die or can do some pipe busy checking
		//http://msdn.microsoft.com/en-us/library/aa365592(VS.85).aspx
		WriteFile(hPipe,pParam,strlen((char *)pParam),&cbWritten,NULL);
		exit(EXIT_SUCCESS);

	}

	printf("Idle Thread Down_Should never happeN_\n");
}

void __cdecl GraphEventsThreadProc(void * pParam){

    //process graph events
    long evCode;
    LONG_PTR param1, param2;

    while(SUCCEEDED(pEvent->GetEvent(&evCode, &param1, &param2, INFINITE)))
    {
        switch(evCode){
            case EC_COMPLETE:
                std::string toPlay = _list->next_item();
                if(reinit_mediaplayer(toPlay,false))
               		printf("Playing_auto: %s\n",toPlay.c_str());
                else
               		printf("Nothing to play. I am bored.\n");

                break;
        }
    }
}

void init_application_settings()
{
	result_set * settings = _list->get_row_map("SELECT * FROM settings WHERE type = 'KEYMAP'");
	if(settings->size())
	{
	}
	else
	{//no settings
		_list->raw_execute("CREATE TABLE settings (type VARCHAR(10), keycode INT, description VARCHAR(120))");
		const std::map<int,int> * kmap = _application->get_key_map();
		std::map<int,int>::const_iterator p = kmap->begin();
		while(p != kmap->end())
		{
			std::string sql = "INSERT INTO settings SELECT 'KEYMAP',"+
				       application::convert_to_string(p->first)+ ",'"+
					  (KeyMapValue::get_instance())->get_description(p->second)+"'";
			_list->raw_execute(sql.c_str());
			p++;
		}
	}
}

int main(int argc, char * argv[])
{
	printf("MiniPlayer v 0.01\n");
	printf("_press_ h for help\n");
	_list = new playlist;

	for(int i = 1; i< argc; i++)
		_list->add_item(argv[i],-1);
     
	std::string toPlay = _list->next_item();

	_application = application::get_instance();
    init_application_settings();

	std::string param;
	for(int i=1;i<argc;i++){
		param+=argv[i];
		param+=";";
	}
	_beginthread(IdleThreadProc,0,(void *)param.c_str());

    // Initialize the COM library.
    HRESULT hr = CoInitialize(NULL);
    if (FAILED(hr))
    {
        printf("ERROR - Could not initialize COM library");
        return EXIT_FAILURE;
    }

      // Create the filter graph manager and query for interfaces.
    hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, 
                        IID_IGraphBuilder, (void **)&pGraph);
    if (FAILED(hr))
    {
        printf("ERROR - Could not create the Filter Graph Manager.");
        return EXIT_FAILURE;
    }
    hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
    hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
    hr = pGraph->QueryInterface(IID_IBasicAudio,(void **)&pBasicAudio);

    _mediaEventsThread = (HANDLE) _beginthread(GraphEventsThreadProc,0,NULL);

	_isPaused = false;
    _isMuted = -10000;

	if (!toPlay.empty())
	{
		printf("Playing: %s\n",toPlay.c_str());
        hr = pGraph->RenderFile(GetWChar(toPlay.c_str()), NULL);
        if (SUCCEEDED(hr))
        {
            // Run the graph.
            hr = pControl->Run();
            while(true)
            {
		            char ch = getch();
		            ProcessInput(ch);
            }
               
	    }
    }
	else
	{
		printf("ERROR [Empty file name].\n");
	}

            pControl->Release();
            pEvent->Release();
            pBasicAudio->Release();
            pGraph->Release();
    CoUninitialize();

	return EXIT_SUCCESS;
}
